package edu.northwestern.cbits.purple_robot_manager.activities;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONException;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager.LayoutParams;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.RadioGroup.OnCheckedChangeListener;
import android.widget.ScrollView;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import edu.emory.mathcs.backport.java.util.Collections;
import edu.northwestern.cbits.purple_robot_manager.R;
import edu.northwestern.cbits.purple_robot_manager.logging.LogManager;
import edu.northwestern.cbits.purple_robot_manager.scripting.JavaScriptEngine;
@SuppressLint("SimpleDateFormat")
public class LabelActivity extends AppCompatActivity
{
public static final String TIMESTAMP = "LABEL TIMESTAMP";
public static final String LABEL_CONTEXT = "LABEL_CONTEXT";
public static final String LABEL_KEY = "LABEL_KEY";
public static final String LABEL_DEFINITIONS = "LABEL_DEFINITIONS";
public static final String INSTRUCTIONS = "LABEL_INSTRUCTIONS";
private static final String REMEMBER_LABEL = "label_remember_values";
private static final boolean REMEMBER_LABEL_DEFAULT = false;
private static final String REMEMBER_LABEL_NAME = "label_remember_name";
private static final String REMEMBER_LABEL_VALUE = "label_remember_value";
private double _timestamp = 0;
private String _labelContext = null;
private final HashMap<String, Object> _values = new HashMap<>();
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
this.setContentView(R.layout.layout_label_activity);
getWindow().setLayout(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
this.getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
private String[] savedLabels()
{
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
String[] labels = new String[0];
try
{
// TODO: Pull into strings.xml...
JSONArray jsonLabels = new JSONArray(prefs.getString("list_label_values", "['Activity', 'Location', 'Social Context']"));
labels = new String[jsonLabels.length()];
for (int i = 0; i < jsonLabels.length(); i++)
{
labels[i] = jsonLabels.getString(i);
}
}
catch (JSONException e)
{
LogManager.getInstance(this).logException(e);
}
return labels;
}
private String[] savedValues()
{
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
String[] values = new String[0];
try
{
JSONArray jsonValues = new JSONArray(prefs.getString("list_value_values", "[]"));
values = new String[jsonValues.length()];
for (int i = 0; i < jsonValues.length(); i++)
{
values[i] = jsonValues.getString(i);
}
}
catch (JSONException e)
{
LogManager.getInstance(this).logException(e);
}
return values;
}
private void saveLabels(String[] labels)
{
JSONArray array = new JSONArray();
for (String label : labels)
array.put(label);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
Editor e = prefs.edit();
e.putString("list_label_values", array.toString());
e.commit();
}
private void saveValues(String[] values)
{
JSONArray array = new JSONArray();
for (String value : values)
array.put(value);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
Editor e = prefs.edit();
e.putString("list_value_values", array.toString());
e.commit();
}
@Override
protected void onNewIntent(Intent intent)
{
super.onNewIntent(intent);
this.setIntent(intent);
}
@Override
@SuppressWarnings("deprecation")
protected void onResume()
{
super.onResume();
Bundle extras = this.getIntent().getExtras();
if (extras.containsKey(LabelActivity.TIMESTAMP))
this._timestamp = extras.getDouble(LabelActivity.TIMESTAMP);
else
this._timestamp = System.currentTimeMillis();
if (extras.containsKey(LabelActivity.LABEL_CONTEXT))
this._labelContext = extras.getString(LabelActivity.LABEL_CONTEXT);
else
this._labelContext = this.getString(R.string.label_unknown_context);
ActionBar actionBar = this.getSupportActionBar();
if (extras.containsKey(LabelActivity.INSTRUCTIONS))
{
final TextView instructions = (TextView) this.findViewById(R.id.text_label_message);
instructions.setText(extras.getString(LabelActivity.INSTRUCTIONS));
}
final TextView contextText = (TextView) this.findViewById(R.id.text_label_context);
contextText.setText(String.format(this.getString(R.string.label_context), this._labelContext));
Date d = new Date((long) this._timestamp);
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss, MMM d");
HashMap<String, Object> payload = new HashMap<>();
final AutoCompleteTextView value = (AutoCompleteTextView) this.findViewById(R.id.text_value_text);
final AutoCompleteTextView label = (AutoCompleteTextView) this.findViewById(R.id.text_label_text);
final CheckBox remember = (CheckBox) this.findViewById(R.id.check_remember_values);
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
if (extras.containsKey(LabelActivity.LABEL_DEFINITIONS))
{
label.setVisibility(View.GONE);
value.setVisibility(View.GONE);
remember.setVisibility(View.GONE);
LinearLayout labelLayout = (LinearLayout) this.findViewById(R.id.layout_label);
labelLayout.removeAllViews();
Bundle definitions = extras.getBundle(LabelActivity.LABEL_DEFINITIONS);
HashMap<String, String> sortedMap = new HashMap<>();
for (String key : definitions.keySet())
{
Bundle field = definitions.getBundle(key);
String fieldName = field.getString("name");
double weight = field.getDouble("weight", 0.0);
sortedMap.put(weight + "_" + fieldName, fieldName);
}
ArrayList<String> keyList = new ArrayList<>();
for (String key : sortedMap.keySet())
{
if (key != null)
keyList.add(key);
}
Collections.sort(keyList);
for (String field : keyList)
{
field = sortedMap.get(field);
Bundle fieldDef = definitions.getBundle(field);
if (fieldDef != null)
{
final TextView fieldName = new TextView(this);
if (fieldDef.containsKey("prompt"))
fieldName.setText(fieldDef.getString("prompt"));
else
fieldName.setText(field);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
params.setMargins(8, 24, 8, 8);
fieldName.setLayoutParams(params);
labelLayout.addView(fieldName);
String fieldType = fieldDef.getString("type");
final LabelActivity me = this;
final String fieldLabel = field;
final String fieldPrompt = fieldName.getText().toString();
if (fieldType.equalsIgnoreCase("real"))
{
SeekBar seekBar = new SeekBar(this);
params = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
params.setMargins(8, 8, 8, 8);
seekBar.setLayoutParams(params);
final double min = fieldDef.getDouble("min", 1.0);
final double max = fieldDef.getDouble("max", 10.0);
final int step = (int) fieldDef.getDouble("step", 1.0);
double defaultValue = (((min + max) / 2) - min);
float lastValue = prefs.getFloat("label_field_" + field, (float) (defaultValue - (defaultValue % step)));
seekBar.setMax((int) (max - min));
seekBar.setProgress((int) lastValue);
me._values.put(fieldLabel, lastValue);
seekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener()
{
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
{
if (fromUser)
{
progress = progress - (progress % step);
seekBar.setProgress(progress);
fieldName.setText(fieldPrompt + ": " + ((int) (min + progress)));
me._values.put(fieldLabel, (float) (min + progress));
}
}
@Override
public void onStartTrackingTouch(SeekBar arg0)
{
}
@Override
public void onStopTrackingTouch(SeekBar seekBar)
{
}
});
labelLayout.addView(seekBar);
}
else if (fieldType.equalsIgnoreCase("nominal"))
{
if (fieldDef.containsKey("values") && fieldDef.getStringArrayList("values").size() > 0)
{
ScrollView scroller = new ScrollView(this);
params = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, 0, 1);
scroller.setLayoutParams(params);
String lastValue = prefs.getString("label_field_" + field, null);
RadioGroup radios = new RadioGroup(this);
LayoutInflater inflater = this.getLayoutInflater();
for (String nominalValue : fieldDef.getStringArrayList("values"))
{
RadioButton radio = (RadioButton) inflater.inflate(R.layout.layout_radio_button, null);
radio.setText(nominalValue);
radios.addView(radio);
if (lastValue != null && nominalValue.equalsIgnoreCase(lastValue))
{
if (radio.isChecked() == false)
radio.toggle();
}
}
if (lastValue != null)
me._values.put(fieldLabel, lastValue);
radios.setOnCheckedChangeListener(new OnCheckedChangeListener()
{
@Override
public void onCheckedChanged(RadioGroup radios, int checkId)
{
if (checkId != -1)
{
RadioButton button = (RadioButton) radios.findViewById(checkId);
me._values.put(fieldLabel, button.getText());
}
else
me._values.remove(fieldLabel);
}
});
scroller.addView(radios);
labelLayout.addView(scroller);
}
}
else if (fieldType.equalsIgnoreCase("text"))
{
AutoCompleteTextView textField = new AutoCompleteTextView(this);
if (fieldDef.containsKey("placeholder"))
textField.setHint(fieldDef.getString("placeholder"));
String valuesList = prefs.getString("label_field_" + field + "_saved_values", "");
ArrayList<String> savedValues = new ArrayList<>();
for (String saved : valuesList.split(";"))
savedValues.add(saved);
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_dropdown_item_1line, savedValues);
textField.setAdapter(adapter);
textField.setThreshold(1);
params = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
params.setMargins(8, 8, 8, 8);
textField.setLayoutParams(params);
textField.addTextChangedListener(new TextWatcher()
{
@Override
public void afterTextChanged(Editable s)
{
me._values.put(fieldLabel, s.toString());
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after)
{
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count)
{
}
});
labelLayout.addView(textField);
}
}
}
}
else
{
label.setVisibility(View.VISIBLE);
value.setVisibility(View.VISIBLE);
remember.setVisibility(View.VISIBLE);
boolean rememberLabel = prefs.getBoolean(LabelActivity.REMEMBER_LABEL, LabelActivity.REMEMBER_LABEL_DEFAULT);
remember.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton button, boolean checked) {
}
});
remember.setChecked(rememberLabel);
remember.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener()
{
@Override
public void onCheckedChanged(CompoundButton button, boolean checked)
{
Editor e = prefs.edit();
e.putBoolean(LabelActivity.REMEMBER_LABEL, checked);
e.commit();
}
});
if (rememberLabel)
{
String labelName = prefs.getString(LabelActivity.REMEMBER_LABEL_NAME, null);
String labelValue = prefs.getString(LabelActivity.REMEMBER_LABEL_VALUE, null);
if (labelName != null)
label.setText(labelName);
if (labelValue != null)
value.setText(labelValue);
}
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_dropdown_item_1line, this.savedLabels());
label.setAdapter(adapter);
if (extras.containsKey(LabelActivity.LABEL_KEY))
{
label.setText(extras.getString(LabelActivity.LABEL_KEY));
payload.put("label", extras.getString(LabelActivity.LABEL_KEY));
}
ArrayAdapter<String> valueAdapter = new ArrayAdapter<>(this, android.R.layout.simple_dropdown_item_1line, this.savedValues());
value.setAdapter(valueAdapter);
value.requestFocus();
}
actionBar.setTitle(this._labelContext);
actionBar.setSubtitle(sdf.format(d));
payload.put("label_time", this._timestamp);
payload.put("label_context", this._labelContext);
LogManager.getInstance(this).log("pr_label_prompt", payload);
}
@Override
protected void onPause()
{
super.onPause();
Bundle extras = this.getIntent().getExtras();
HashMap<String, Object> payload = new HashMap<>();
if (extras.containsKey(LabelActivity.LABEL_KEY))
payload.put("label", extras.getString(LabelActivity.LABEL_KEY));
payload.put("label_time", this._timestamp);
payload.put("label_context", this._labelContext);
LogManager.getInstance(this).log("pr_label_dismissed", payload);
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
MenuInflater inflater = this.getMenuInflater();
inflater.inflate(R.menu.menu_label_activity, menu);
return true;
}
@Override
@SuppressLint("ValidFragment")
public boolean onOptionsItemSelected(MenuItem item)
{
final int itemId = item.getItemId();
if (itemId == android.R.id.home)
this.finish();
else if (itemId == R.id.menu_accept_label)
{
Bundle extras = this.getIntent().getExtras();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
if (extras.containsKey(LabelActivity.LABEL_DEFINITIONS))
{
Editor e = prefs.edit();
for (String key : this._values.keySet())
{
Object value = this._values.get(key);
JavaScriptEngine js = new JavaScriptEngine(this);
js.emitReading(key, value.toString());
HashMap<String, Object> payload = new HashMap<>();
payload.put("label_time", this._timestamp);
payload.put("label", key);
payload.put("label_value", value.toString());
LogManager.getInstance(this).log("pr_label_submit", payload);
if (value instanceof Float)
e.putFloat("label_field_" + key, (Float) value);
else
{
e.putString("label_field_" + key, value.toString());
String valuesList = prefs.getString("label_field_" + key + "_saved_values", "");
ArrayList<String> savedValues = new ArrayList<>();
for (String saved : valuesList.split(";"))
savedValues.add(saved);
savedValues.remove(value.toString());
savedValues.add(0, value.toString());
StringBuilder sb = new StringBuilder();
for (String savedValue : savedValues)
{
if (sb.length() > 0)
sb.append(";");
sb.append(savedValue);
}
e.putString("label_field_" + key + "_saved_values", sb.toString());
}
}
e.commit();
this.finish();
}
else
{
EditText keyText = (EditText) this.findViewById(R.id.text_label_text);
EditText valueText = (EditText) this.findViewById(R.id.text_value_text);
String key = keyText.getText().toString();
String value = valueText.getText().toString();
if (key != null && value != null && key.length() > 0 && value.length() > 0)
{
boolean rememberLabel = prefs.getBoolean(LabelActivity.REMEMBER_LABEL, LabelActivity.REMEMBER_LABEL_DEFAULT);
if (rememberLabel)
{
Editor e = prefs.edit();
e.putString(LabelActivity.REMEMBER_LABEL_NAME, key);
e.putString(LabelActivity.REMEMBER_LABEL_VALUE, value);
e.commit();
}
JavaScriptEngine js = new JavaScriptEngine(this);
js.emitReading(key, value);
List<String> labels = new ArrayList<>(Arrays.asList(this.savedLabels()));
labels.remove(key);
labels.add(0, key);
String[] labelsArray = labels.toArray(new String[0]);
this.saveLabels(labelsArray);
List<String> values = new ArrayList<>(Arrays.asList(this.savedValues()));
values.remove(value);
values.add(0, value);
String[] valuesArray = values.toArray(new String[0]);
this.saveValues(valuesArray);
HashMap<String, Object> payload = new HashMap<>();
payload.put("label_time", this._timestamp);
payload.put("label", key);
payload.put("label_value", value);
LogManager.getInstance(this).log("pr_label_submit", payload);
this.finish();
}
else
{
FragmentManager manager = this.getSupportFragmentManager();
final LabelActivity me = this;
DialogFragment dialog = new DialogFragment()
{
@Override
public Dialog onCreateDialog(Bundle savedInstanceState)
{
AlertDialog.Builder builder = new AlertDialog.Builder(me);
builder.setTitle(R.string.title_missing_label);
builder.setMessage(R.string.message_missing_label);
builder.setPositiveButton(R.string.button_ok, new OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int arg)
{
}
});
return builder.create();
}
};
dialog.show(manager, "label_error");
}
}
}
return true;
}
}